package com.tianji.learning.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.tianji.common.utils.BeanUtils;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;

/**
 * @author Waker
 * @description:
 * @create 2025-10-23-15:25
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class LearningRecordDelayTaskHandler implements ApplicationContextAware, CommandLineRunner, EnvironmentAware, InitializingBean {

    private final LearningRecordMapper learningRecordMapper;
    private final LearningLessonMapper learningLessonMapper;
    private final StringRedisTemplate redisTemplate;
    private final String KEY_TEMPLATE = "learning:record:{}";
    private DelayQueue<DelayTask<DelayTaskData>> delayQueue = new DelayQueue<>();
    private static Boolean flag = true;


    /**
     * 从缓存中查询学习记录
     *
     * @param lessonId
     * @param sectionId
     * @return
     */
    public LearningRecord readLearningRecordFromCache(Long lessonId, Long sectionId) {
        log.info("正在从缓存中查询学习记录，课表ID:{}，小节ID:{}", lessonId, sectionId);
        // 1. 组装大key
        String key = StrUtil.format(KEY_TEMPLATE, lessonId);

        // 2. 组装hash key
        String hashKey = String.valueOf(sectionId);

        // 3. 发起查询，得到hash value
        String hashValue = (String) redisTemplate.opsForHash().get(key, hashKey);
        if (StrUtil.isBlank(hashValue)) {
            log.warn("缓存中没有查询到学习记录，课表ID:{}，小节ID:{}", lessonId, sectionId);
            return null;
        }

        // 4. 将hash value转为LearningRecord对象
        return JSONUtil.toBean(hashValue, LearningRecord.class);
    }

    /**
     * 将数据库查询的结果写入缓存
     *
     * @param dbRecord
     */
    public void writeLearningRecordToCache(LearningRecord dbRecord) {
        log.info("正在将学习记录写入缓存，课表ID:{}，小节ID:{}", dbRecord.getLessonId(), dbRecord.getSectionId());
        // 1. 组装大key
        String key = StrUtil.format(KEY_TEMPLATE, dbRecord.getLessonId());

        // 2. 组装hash key
        String hashKey = String.valueOf(dbRecord.getSectionId());

        // 3. 组装hash value
        String hashValue = JSONUtil.toJsonStr(BeanUtils.toBean(dbRecord, RedisRecordData.class));

        // 4. 发起写入缓存
        redisTemplate.opsForHash().put(key, hashKey, hashValue);
        redisTemplate.expire(key, Duration.ofMinutes(1));
    }

    /**
     * 将学习记录暂存到redis，并提交一个延时任务
     *
     * @param learningRecord
     */
    public void addLearningRecordAndDelayCheckTask(LearningRecord learningRecord) {
        log.info("正在将学习记录暂存到redis，课表ID:{}，小节ID:{}", learningRecord.getLessonId(), learningRecord.getSectionId());
        // 1. 暂存播放记录至redis
        writeLearningRecordToCache(learningRecord);

        // 2. 提交一个延时检测任务
        delayQueue.add(DelayTask.of(Duration.ofSeconds(20),BeanUtils.toBean(learningRecord, DelayTaskData.class)));
    }

    /**
     * 删除redis缓存,保证redis Mysql数据双写一致性
     *
     * @param lessonId
     * @param sectionId
     */
    public void removeLearningRecordCache(Long lessonId, Long sectionId) {
        log.info("正在删除redis缓存，课表ID:{}，小节ID:{}", lessonId, sectionId);

        // 1. 组装大key
        String key = StrUtil.format(KEY_TEMPLATE, lessonId);

        // 2. 组装hash key
        String hashKey = String.valueOf(sectionId);

        // 3. 删除redis缓存
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    @PostConstruct
    public void init(){
//        new Thread(()-> handleDelayTask()).start();
        CompletableFuture.runAsync(this::handleDelayTask);
    }

    @PreDestroy
    public void destroy(){
        flag = false;
    }

    /**                 15s             30s           45s
     * 处理延时检测任务         35s(15s)       50s(30s)       65s(45s)
     */
    private void handleDelayTask() {
        log.info("开始执行播放进度延时检测任务扫描...........");
        while (flag) {
            try {
                // 1. 从队列中尝试take 获取延时检测任务
                DelayTask<DelayTaskData> delayTask = delayQueue.take();
                DelayTaskData data = delayTask.getData();
                log.info("获取到延时检测任务，任务数据：{}",data);

                // 2. 从redis中查询实时播放进度
                LearningRecord redisRecord = readLearningRecordFromCache(data.getLessonId(), data.getSectionId());

                // 3. 比较redis中的实时播放进度和延时任务中的本地进度
                // 3.1 如果不一致，证明用户还在观看，直接跳过
                if (ObjectUtil.isEmpty(redisRecord) || ObjectUtil.notEqual(redisRecord.getMoment(), data.getMoment())){
                    continue;
                }

                // 3.2 如果一致，证明用户已经停止观看，将redis中的实时进度 更新到 学习记录表 并更新 课表数据
                // 3.2.1 更新学习记录表（moment）
                redisRecord.setFinished(null);
                learningRecordMapper.updateById(redisRecord);

                // 3.2.2 更新课表数据（最近学习小节ID，最近学习时间）
                LearningLesson lesson = new LearningLesson();
                lesson.setId(data.getLessonId());
                lesson.setLatestSectionId(data.getSectionId());
                lesson.setLatestLearnTime(LocalDateTime.now().minusSeconds(20));
                learningLessonMapper.updateById(lesson);

            } catch (Exception e) {
                log.error("处理播放进度延时检测任务异常", e);
                throw new RuntimeException(e);
            }
        }

    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    @Override
    public void run(String... args) throws Exception {

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

    }

    @Override
    public void setEnvironment(Environment environment) {

    }

    @Data
    class RedisRecordData{

        /**
         * 学习记录的id
         */
        private Long id;

        /**
         * 视频的当前观看时间点，单位秒
         */
        private Integer moment;

        /**
         * 是否完成学习，默认false
         */
        private Boolean finished;
    }

    @Data
    class DelayTaskData{

        /**
         * 课表ID
         */
        private Long lessonId;

        /**
         * 小节ID
         */
        private Long sectionId;

        /**
         * 视频的当前观看时间点，单位秒
         */
        private Integer moment;
    }

}
